// 段页表基址mask
#define PAGE_TABLE_L1_BASE_ADDR_MASK (0xffffc000)
// 由虚拟地址到段页表项索引
#define VIRT_TO_PTE_L1_INDEX(addr) (((addr) & 0xfff00000) >> 18)
// 段页表项表示的页不使用cache和write_buf
#define PTE_L1_SECTION_NO_CACHE_AND_WB (0x0 << 2)
// 段页表项默认域标识
#define PTE_L1_SECTION_DOMAIN_DEFAULT (0x0 << 5)
// 段页表项默认权限标识
#define PTE_ALL_AP_L1_SECTION_DEFAULT (0x1 << 10)
// 段页表项获取物理地址mask
#define PTE_L1_SECTION_PADDR_BASE_MASK (0xfff00000)
// 一级页表标识
#define PTE_BITS_L1_SECTION (0x2)
// 段页表基地址
#define L1_PTR_BASE_ADDR 0x30700000

// 物理内存地址
#define PHYSICAL_MEM_ADDR 0x30000000
// 虚拟内存地址
#define VIRTUAL_MEM_ADDR 0x30000000
// 物理内存大小
#define MEM_MAP_SIZE 0x800000

// 物理外设地址
#define PHYSICAL_IO_ADDR 0x48000000
// 虚拟外设地址
#define VIRTUAL_IO_ADDR 0xc8000000
// 外设内存大小
#define IO_MAP_SIZE 0x18000000

// 异常向量虚拟地址
#define VECTOR_VIRTUAL_ADDR 0x0
// 异常向量物理地址
#define VECTOR_PHYSICAL_ADDR 0x30000000

void start_mmu(void)
{
	unsigned int ttb = L1_PTR_BASE_ADDR;

	asm(
		"mcr p15,0,%0,c2,c0,0\n" /* set base address of page table*/
		"mvn r0,#0\n"
		"mcr p15,0,r0,c3,c0,0\n" /* enable all region access*/

		"mov r0,#0x1\n"
		"mcr p15,0,r0,c1,c0,0\n" /* set back to control register */
		"mov r0,r0\n"
		"mov r0,r0\n"
		"mov r0,r0\n"
		:
		: "r"(ttb)
		: "r0");
}

// 由物理内存获取段页表项
unsigned int gen_l1_pte(unsigned int paddr)
{
	return (paddr & PTE_L1_SECTION_PADDR_BASE_MASK) | PTE_BITS_L1_SECTION;
}

// 由段页表基地址和虚拟地址获取段页表项地址
unsigned int gen_l1_pte_addr(unsigned int baddr, unsigned int vaddr)
{
	return (baddr & PAGE_TABLE_L1_BASE_ADDR_MASK) | VIRT_TO_PTE_L1_INDEX(vaddr);
}

// 映射物理内存空间\外设内存空间到虚拟内存
void init_sys_mmu(void)
{
	unsigned int pte;
	unsigned int pte_addr;
	int j;

	// 异常向量表映射
	for (j = 0; j < MEM_MAP_SIZE >> 20; j++)
	{
		pte = gen_l1_pte(VECTOR_PHYSICAL_ADDR + (j << 20));
		pte |= PTE_ALL_AP_L1_SECTION_DEFAULT;
		pte |= PTE_L1_SECTION_NO_CACHE_AND_WB;
		pte |= PTE_L1_SECTION_DOMAIN_DEFAULT;
		pte_addr = gen_l1_pte_addr(L1_PTR_BASE_ADDR, VECTOR_VIRTUAL_ADDR + (j << 20));
		*(volatile unsigned int *)pte_addr = pte;
	}

	// 物理内存大小为8M,一级页表的页大小为1M,一共8页
	for (j = 0; j < MEM_MAP_SIZE >> 20; j++)
	{
		pte = gen_l1_pte(PHYSICAL_MEM_ADDR + (j << 20));
		pte |= PTE_ALL_AP_L1_SECTION_DEFAULT;
		pte |= PTE_L1_SECTION_NO_CACHE_AND_WB;
		pte |= PTE_L1_SECTION_DOMAIN_DEFAULT;
		pte_addr = gen_l1_pte_addr(L1_PTR_BASE_ADDR, VIRTUAL_MEM_ADDR + (j << 20));
		*(volatile unsigned int *)pte_addr = pte;
	}

	// 外设地址空间映射到虚拟内存,0x180页
	for (j = 0; j < IO_MAP_SIZE >> 20; j++)
	{
		pte = gen_l1_pte(PHYSICAL_IO_ADDR + (j << 20));
		pte |= PTE_ALL_AP_L1_SECTION_DEFAULT;
		pte |= PTE_L1_SECTION_NO_CACHE_AND_WB;
		pte |= PTE_L1_SECTION_DOMAIN_DEFAULT;
		pte_addr = gen_l1_pte_addr(L1_PTR_BASE_ADDR, VIRTUAL_IO_ADDR + (j << 20));
		*(volatile unsigned int *)pte_addr = pte;
	}
}

// 建立物理地址和虚拟地址bytes字节的映射关系
void mmu_remaping(unsigned int pAddr, unsigned int vAddr, unsigned int bytes)
{
	unsigned int pte;
	unsigned int pte_addr;
	int j;
	for (j = 0; j < (bytes >> 20); j++)
	{
		pte = gen_l1_pte(pAddr + (j << 20));
		pte_addr = gen_l1_pte_addr(L1_PTR_BASE_ADDR, vAddr + (j << 20));
		*(volatile unsigned int *)pte_addr = pte;
	}
}

void test_mmu(void)
{
	const char *p = "test_mmu\n";
	while (*p)
	{
		*(volatile unsigned int *)0xd0000020 = *p;
		p++;
	};
}